package com.cmc.cloud.cmclink.doc.service.impl;

import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;

import javax.annotation.Resource;

import com.cmc.cloud.cmclink.doc.entity.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.annotation.Validated;

import com.cmc.cloud.cmclink.doc.constants.errorconstant.DgProhibitedErrorCodeConstants;
import com.cmc.cloud.cmclink.doc.constants.errorconstant.TerminalDgTeuLimitedErrorCodeConstants;
import com.cmc.cloud.cmclink.doc.convert.DgProhibitedConvert;
import com.cmc.cloud.cmclink.doc.convert.DgProhibitedItemConvert;
import com.cmc.cloud.cmclink.doc.dto.DgProhibitedGetDto;
import com.cmc.cloud.cmclink.doc.dto.DgProhibitedItemUpdateDto;
import com.cmc.cloud.cmclink.doc.dto.DgProhibitedUpdateDto;
import com.cmc.cloud.cmclink.doc.mapper.DgProhibitedItemMapper;
import com.cmc.cloud.cmclink.doc.mapper.DgProhibitedMapper;
import com.cmc.cloud.cmclink.doc.rpcservice.NucTransferService;
import com.cmc.cloud.cmclink.doc.service.DgProhibitedItemService;
import com.cmc.cloud.cmclink.doc.service.DgProhibitedService;
import com.cmc.cloud.cmclink.doc.util.sepcial.CommonUtil;
import com.cmc.cloud.cmclink.doc.vo.special.dgprohibiteditemvo.DgProhibitedItemBaseVO;
import com.cmc.cloud.cmclink.doc.vo.special.dgprohibitedvo.*;
import com.cmes.framework.common.exception.util.ServiceExceptionUtil;
import com.cmes.framework.common.pojo.PageResult;
import com.cmes.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.cmes.platform.system.nuc.dto.AdminUserDto;

import lombok.extern.slf4j.Slf4j;

/**
 * 危险品禁接清单 Service 实现类
 *
 * @author 集运管理员
 */
@Service
@Validated
@Slf4j
public class DgProhibitedServiceImpl implements DgProhibitedService {

    @Resource
    private DgProhibitedMapper dgProhibitedMapper;

    @Resource
    private DgProhibitedItemMapper dgProhibitedItemMapper;

    @Resource
    private DgProhibitedItemService dgProhibitedItemService;

    @Resource
    private NucTransferService nucTransferService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void createDgProhibited(DgProhibitedCreateReqVO createReqVO) {
        DgProhibitedBaseVO baseVO = createReqVO.getDgProhibitedBaseVO();
        if (baseVO.getStartDate().isAfter(baseVO.getEndDate())) {
            throw ServiceExceptionUtil.exception(DgProhibitedErrorCodeConstants.DG_PROHIBITED_START_GT_END);
        }

        // 校验有效期是否存在重叠
        if (dateOverlap(baseVO)) {
            throw ServiceExceptionUtil.exception(DgProhibitedErrorCodeConstants.DG_PROHIBITED_DATE_OVERLAP);
        }

        // 插入
        DgProhibitedDO dgProhibited = DgProhibitedConvert.INSTANCE.convert(baseVO);
        dgProhibitedMapper.insert(dgProhibited);

        // 插入子表
        List<DgProhibitedItemBaseVO> dgProhibitedItemBaseVOList = createReqVO.getDgProhibitedItems();
        if (!CollectionUtils.isEmpty(dgProhibitedItemBaseVOList)) {
            dgProhibitedItemBaseVOList.forEach(item -> item.setMainId(dgProhibited.getId()));
            List<DgProhibitedItemDO> dgProhibitedItemDOList =
                DgProhibitedItemConvert.INSTANCE.convertList(dgProhibitedItemBaseVOList);
            dgProhibitedItemMapper.insertBatch(dgProhibitedItemDOList);
        }
    }

    public Boolean dateOverlap(DgProhibitedBaseVO baseVo) {
        return dateOverlap(baseVo, null);
    }

    private Boolean dateOverlap(DgProhibitedBaseVO baseVo, Long id) {
        LambdaQueryWrapperX<DgProhibitedDO> queryWrapperX = new LambdaQueryWrapperX<>();
        queryWrapperX.eqIfPresent(DgProhibitedDO::getListType, baseVo.getListType())
            .eqIfPresent(DgProhibitedDO::getCodeType, baseVo.getCodeType())
            .eqIfPresent(DgProhibitedDO::getCodeValue, baseVo.getCodeValue())
            .eqIfPresent(DgProhibitedDO::getListType, baseVo.getListType())
            .eqIfPresent(DgProhibitedDO::getImoClass, baseVo.getImoClass());
        if (id != null) {
            queryWrapperX.ne(DgProhibitedDO::getId, id);
        }
        List<DgProhibitedDO> dgProhibitedDOList = dgProhibitedMapper.selectList(queryWrapperX);
        for (DgProhibitedDO dgDO : dgProhibitedDOList) {
            if (CommonUtil.dateOverlap(dgDO.getStartDate(), dgDO.getEndDate(), baseVo.getStartDate(),
                baseVo.getEndDate())) {
                return true;
            }
        }

        return false;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateDgProhibited(DgProhibitedUpdateReqVO updateReqVO) {
        // 校验存在
        DgProhibitedUpdateDto dgUpdateDto = updateReqVO.getDgProhibitedUpdateDto();
        validateDgProhibitedExists(dgUpdateDto.getId());

        // 校验开始生效时间是否小于结束时间
        if (dgUpdateDto.getStartDate().isAfter(dgUpdateDto.getEndDate())) {
            throw ServiceExceptionUtil.exception(DgProhibitedErrorCodeConstants.DG_PROHIBITED_START_GT_END);
        }

        // 校验有效期是否存在重叠
        if (dateOverlap(dgUpdateDto, dgUpdateDto.getId())) {
            throw ServiceExceptionUtil.exception(DgProhibitedErrorCodeConstants.DG_PROHIBITED_DATE_OVERLAP);
        }

        // 更新主表
        DgProhibitedDO updateObj = DgProhibitedConvert.INSTANCE.convertUpdate(dgUpdateDto);
        dgProhibitedMapper.updateById(updateObj);

        // 更新子表
        List<DgProhibitedItemUpdateDto> dgProhibitedItemUpdatesDto = updateReqVO.getDgProhibitedItemUpdatesDto();
        if (dgProhibitedItemUpdatesDto == null) {
            dgProhibitedItemUpdatesDto = new ArrayList<>();
        }
        dgProhibitedItemUpdatesDto.forEach(item -> item.setMainId(dgUpdateDto.getId()));
        dgProhibitedItemService.updateAllDgProhibitedItem(dgProhibitedItemUpdatesDto, dgUpdateDto.getId());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteDgProhibited(Long id) {
        // 校验存在
        validateDgProhibitedExists(id);

        // 删除
        dgProhibitedMapper.deleteById(id);

        // 删除子表
        dgProhibitedItemService.getDgProhibitedItemsByMainId(id);
    }

    private void validateDgProhibitedExists(Long id) {
        if (dgProhibitedMapper.selectById(id) == null) {
            throw ServiceExceptionUtil.exception(DgProhibitedErrorCodeConstants.DG_PROHIBITED_NOT_EXISTS);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public DgProhibitedGetDto getDgProhibited(Long id) {
        DgProhibitedDO dgProhibitedDO = dgProhibitedMapper.selectById(id);
        List<DgProhibitedItemDO> dgProhibitedItemsDO = dgProhibitedItemService.getDgProhibitedItemsByMainId(id);
        updateSubCreatorAndUpdater(dgProhibitedItemsDO);
        DgProhibitedGetDto dgProhibitedGetDto = new DgProhibitedGetDto();
        dgProhibitedGetDto.setDgProhibitedDO(dgProhibitedDO);
        dgProhibitedGetDto.setDgProhibitedItemsDO(dgProhibitedItemsDO);
        return dgProhibitedGetDto;
    }

    private void updateSubCreatorAndUpdater(List<DgProhibitedItemDO> dgProhibitedItemDOList) {
        if (CollectionUtils.isEmpty(dgProhibitedItemDOList)) {
            return;
        }
        Set<Long> userIds = new HashSet<>();
        for (DgProhibitedItemDO itemDO : dgProhibitedItemDOList) {
            long creatorUserId;
            long updateUserId;
            try {
                creatorUserId = Long.parseLong(itemDO.getCreator());
                updateUserId = Long.parseLong(itemDO.getUpdater());
            } catch (NumberFormatException e) {
                log.error("创建者或者更新者转Long型失败， 创建者: {}, 更新者: {}", itemDO.getCreator(), itemDO.getUpdater());
                continue;
            }

            userIds.add(creatorUserId);
            userIds.add(updateUserId);
        }

        List<AdminUserDto> adminUsers = nucTransferService.getUserProfileByIdList(new ArrayList<>(userIds));
        Map<String, String> userIdToCodeMap = adminUsers.stream()
            .collect(Collectors.toMap(user -> String.valueOf(user.getId()), AdminUserDto::getNickname));
        dgProhibitedItemDOList.forEach(item -> {
            item.setCreator(userIdToCodeMap.getOrDefault(item.getCreator(), item.getCreator()));
            item.setUpdater(userIdToCodeMap.getOrDefault(item.getUpdater(), item.getUpdater()));
        });
    }

    @Override
    public List<DgProhibitedWithSubDO> getDgProhibitedWithSubList(DgProhibitedExcelReqVO excelReqVO) {
        return dgProhibitedMapper.selectAllWithSubList(excelReqVO);
    }

    @Override
    public PageResult<DgProhibitedDO> getDgProhibitedPage(DgProhibitedPageReqVO reqVO) {
        LambdaQueryWrapperX<DgProhibitedDO> queryWrapperX = new LambdaQueryWrapperX<>();
        queryWrapperX.eqIfPresent(DgProhibitedDO::getListType, reqVO.getListType())
            .eqIfPresent(DgProhibitedDO::getCodeValue, reqVO.getCodeValue())
            .eqIfPresent(DgProhibitedDO::getCodeType, reqVO.getCodeType()).orderByAsc(DgProhibitedDO::getCodeType)
            .orderByAsc(DgProhibitedDO::getCodeValue);
        return dgProhibitedMapper.selectPage(reqVO, queryWrapperX);
    }

    @Override
    public void updateEndDates(List<Long> ids, LocalDate endDate) {
        List<DgProhibitedDO> dgDOList = validAndUpdateEndDate(new HashSet<>(ids), endDate);
        dgProhibitedMapper.updateBatch(dgDOList);
    }

    private List<DgProhibitedDO> validAndUpdateEndDate(Set<Long> ids, LocalDate endDate) {
        // 查询所有item
        List<DgProhibitedDO> dgDOList = dgProhibitedMapper.selectList();

        // 设置id,判断更新后startDate是否小于endDate
        List<DgProhibitedDO> limitedDOList = new ArrayList<>();
        for (DgProhibitedDO dgDO : dgDOList) {
            if (ids.contains(dgDO.getId())) {
                if (dgDO.getStartDate().isAfter(endDate)) {
                    throw ServiceExceptionUtil.exception(DgProhibitedErrorCodeConstants.DG_PROHIBITED_START_GT_END);
                }
                dgDO.setEndDate(endDate);
                limitedDOList.add(dgDO);
            }

            validDateOverlap(limitedDOList, dgDOList);
        }

        return limitedDOList;
    }

    /**
     * 判断修改后是否出现生效时间重叠
     *
     * @param dgDOList 修改后的DO列表
     * @param historyDos 数据库原本的DO列表
     */
    private void validDateOverlap(List<DgProhibitedDO> dgDOList, List<DgProhibitedDO> historyDos) {
        for (DgProhibitedDO updateDo : dgDOList) {
            for (DgProhibitedDO historyDo : historyDos) {
                if (updateDo.getId().equals(historyDo.getId())) {
                    continue;
                }

                String updateKey =
                    CommonUtil.buildKey(updateDo.getListType(), updateDo.getCodeType(), updateDo.getCodeValue());
                String historyKey =
                    CommonUtil.buildKey(historyDo.getListType(), historyDo.getCodeType(), historyDo.getCodeValue());
                if (updateKey.equals(historyKey) && CommonUtil.dateOverlap(updateDo.getStartDate(),
                    updateDo.getEndDate(), historyDo.getStartDate(), historyDo.getEndDate())) {
                    throw ServiceExceptionUtil
                        .exception(TerminalDgTeuLimitedErrorCodeConstants.TERMINAL_DG_TEU_DATE_OVERLAP);
                }
            }
        }
    }

    /**
     * 检查是否在名单item中
     *
     * @param listType  名单类型
     * @param codeType  名单代码类型
     * @param codeValue 名单代码值
     * @param dgDO      危险品信息
     * @param cntrDO cntrDO
     * @return 如果在名单中返回true，否则返回false
     */
    @Override
    public DgProhibitedDO isInList(String listType, String codeType, String codeValue, SpecialCargoDgDO dgDO, SpecialCargoCntrDO cntrDO) {
        LocalDate today = LocalDate.now();

        LambdaQueryWrapperX<DgProhibitedDO> queryWrapper = new LambdaQueryWrapperX<>();
        queryWrapper.eq(DgProhibitedDO::getListType, listType)
                .eq(DgProhibitedDO::getCodeType, codeType)
                .eq(DgProhibitedDO::getCodeValue, codeValue)
                // 不限制cntrType null 或空 的 和匹配到的
                .and(wrapper ->
                        wrapper.isNull(DgProhibitedDO::getCntrTypes)
                        .or().eq(DgProhibitedDO::getCntrTypes, "")
                        .or().like(DgProhibitedDO::getCntrTypes, cntrDO.getCntrType()))
                .eq(DgProhibitedDO::getImoClass, dgDO.getImoClass())
                .le(DgProhibitedDO::getStartDate, today)
                .ge(DgProhibitedDO::getEndDate, today);
        
        List<DgProhibitedDO> prohibitedDos = dgProhibitedMapper.selectList(queryWrapper);
        if (prohibitedDos.isEmpty()) {
            return null;
        }
        for (DgProhibitedDO dgProhibitedDO : prohibitedDos) {
            // 检查DG prohibited item
            //不存在子项限制，则返回true
            if(dgProhibitedItemService.existDgProhibitedItemsByMainId(dgProhibitedDO.getId())){
                if(dgProhibitedItemService.existDgProhibitedItemsByMainIdAndDgDto(dgProhibitedDO.getId(),dgDO)){
                    return dgProhibitedDO;
                }
            }else{
                return dgProhibitedDO;
            }
        }

        return null;
    }

}
